package svr

import (
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/gogf/gf/container/gmap"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/net/gtcp"
	"github.com/gogf/gf/os/gtimer"
	"github.com/gogf/gf/util/gconv"
)

var (
	NewServerService = new(serviceServer)
	clientList       = gmap.New(true) //map[clientId string]conn []*gtcp.Conn
	client2UidList   = gmap.New(true) //map[clientId string]uid string
	uid2ClientList   = gmap.New(true) //map[uid string]clientIdList []string
)

type serviceServer struct {
	address                     string
	serverName                  string
	messageHandler              func(conn *gtcp.Conn, message []byte)
	onConnectHandler            func(conn *gtcp.Conn, clientId string)
	onDisConnectHandler         func(conn *gtcp.Conn, clientId string, uid string)
	refuseConnect               bool //是否要拒绝连接
	singleIpAndPortConnectLimit int  //单个IP+端口连接数限制
}

func (s *serviceServer) NewServer() error {
	if len(s.address) == 0 || len(s.serverName) == 0 || s.messageHandler == nil {
		return errors.New("please set address/serverName/messageHandler")
	}

	oldSvr := gtcp.GetServer(s.serverName)
	if oldSvr != nil {
		oldSvr.Close()
	}

	s.singleIpAndPortConnectLimit = 10
	s.refuseConnect = true
	gtimer.SetTimeout(time.Duration(10)*time.Second, func() {
		s.refuseConnect = false
	})

	svr := gtcp.NewServer(s.address, s.OnNewServer, s.serverName)
	err := svr.Run()
	return err
}

func (s *serviceServer) CloseConn(conn *gtcp.Conn) {
	s.onClose(conn)
}

func (s *serviceServer) BindUid(conn *gtcp.Conn, uid string) error {
	clientId, err := s.GetClientIdByConn(conn)
	if err != nil {
		return err
	}

	clientConnListNew := make([]*gtcp.Conn, 0)
	clientConnListOld := clientList.Get(clientId)
	if clientConnListOld != nil {
		clientConnListNew = clientConnListOld.([]*gtcp.Conn)
	}
	clientConnListNew = append(clientConnListNew, conn)
	clientList.Set(clientId, clientConnListNew)

	// 同一个IP+端口最多只允许N个连接，超出时踢掉让客户端重新连接
	if clientList.Size() > s.singleIpAndPortConnectLimit {
		g.Log().Println("[GTCP][ERROR][BindUid]同一个IP+端口连接个数超出限制，超出时踢掉让客户端重新连接:", clientList.Size(), clientId)
		s.onClose(conn)
		return nil
	}

	clientIdListNew := make([]string, 0)
	clientIdListOld := uid2ClientList.Get(uid)
	if clientIdListOld != nil {
		clientIdListNew = clientIdListOld.([]string)
	}
	clientIdListNew = append(clientIdListNew, clientId)
	clientIdListNew = SliceRemoveDuplicates(clientIdListNew)
	uid2ClientList.Set(uid, clientIdListNew)

	client2UidList.Set(clientId, uid)

	return nil
}

func (s *serviceServer) GetClientIdByConn(conn *gtcp.Conn) (string, error) {
	if conn == nil {
		return "", errors.New("conn is nil")
	}
	network := conn.RemoteAddr().Network()
	remoteAddr := conn.RemoteAddr().String()
	if len(network) == 0 || len(remoteAddr) == 0 {
		return "", errors.New("conn.(network/remote addr) is empty")
	}
	return fmt.Sprintf("client:%v:%v", network, remoteAddr), nil
}

func (s *serviceServer) GetUidByConn(conn *gtcp.Conn) (string, error) {
	clientId, err := s.GetClientIdByConn(conn)
	if err != nil {
		return "", err
	}
	return s.GetUidByClientId(clientId), nil
}

func (s *serviceServer) GetUidByClientId(clientId string) string {
	return gconv.String(client2UidList.Get(clientId))
}

func (s *serviceServer) GetClientIdByUid(uid string) []string {
	clientIds := uid2ClientList.Get(uid)
	if clientIds == nil {
		return nil
	}
	return clientIds.([]string)
}

func (s *serviceServer) GetConnByUid(uid string) []*gtcp.Conn {
	clientIds := s.GetClientIdByUid(uid)
	if len(clientIds) == 0 {
		return nil
	}
	connsNew := make([]*gtcp.Conn, 0)
	for _, cid := range clientIds {
		connList := s.GetConnByClientId(cid)
		if connList != nil {
			connsNew = append(connsNew, connList...)
		}
	}
	return connsNew
}

func (s *serviceServer) GetConnByClientId(clientId string) []*gtcp.Conn {
	conn := clientList.Get(clientId)
	if conn == nil {
		return make([]*gtcp.Conn, 0)
	}
	return conn.([]*gtcp.Conn)
}

func (s *serviceServer) SetServerAddress(address string) {
	s.address = address
}

func (s *serviceServer) SetServerName(serverName string) {
	s.serverName = serverName
}

func (s *serviceServer) SetMessageHandler(handler func(conn *gtcp.Conn, message []byte)) {
	s.messageHandler = handler
}

func (s *serviceServer) SetOnConnectHandler(handler func(conn *gtcp.Conn, clientId string)) {
	s.onConnectHandler = handler
}

func (s *serviceServer) SetOnDisConnectHandler(handler func(conn *gtcp.Conn, clientId string, uid string)) {
	s.onDisConnectHandler = handler
}

func (s *serviceServer) GetServerAddress() string {
	return s.address
}

func (s *serviceServer) GetServerName() string {
	return s.serverName
}

func (s *serviceServer) SendToCliendId(clientId string, data []byte) error {
	conns := s.GetConnByClientId(clientId)
	if conns == nil {
		return errors.New("conn is empty")
	}
	for _, conn := range conns {
		err := conn.Send(data)
		if err != nil {
			g.Log().Error("SendToCliendId--ERROR", err)
		}
	}
	return nil
}

func (s *serviceServer) SendToUid(uid string, data []byte) error {
	conns := s.GetConnByUid(uid)
	if len(conns) == 0 {
		return errors.New("conn is empty")
	}
	for _, conn := range conns {
		err := conn.Send(data)
		if err != nil {
			g.Log().Println("[GTCP]SendToUid--ERROR", err)
			// 与连接有关的错误，跳出接收数据for
			if err.Error() == "EOF" || strings.Contains(err.Error(), "connection") {
				g.Log().Println("[GTCP][ERROR]client closed")
				s.CloseSession(conn)
				break
			}
		}
		time.Sleep(time.Duration(30) * time.Millisecond)
	}
	return nil
}

func (s *serviceServer) OnNewServer(conn *gtcp.Conn) {
	err := s.onConnection(conn)
	if err != nil {
		g.Log().Println("[GTCP]OnNewServer--ERROR", err.Error())
		_ = conn.Close()
		return
	}

	defer s.CloseConn(conn)

	var clientId string

	for {
		data, err := conn.Recv(-1)
		if len(data) > 0 {
			if s.messageHandler != nil {
				s.messageHandler(conn, data)
			} else {
				clientId, _ = s.GetClientIdByConn(conn)
				fmt.Printf("[GTCP]rece data from [%v],data = %v\n", clientId, string(data))
			}
		}

		if err != nil {
			clientId, _ = s.GetClientIdByConn(conn)
			g.Log().Println("[GTCP]recv-client-data-ERROR", clientId, err)
			// 与连接有关的错误，跳出接收数据for
			if err.Error() == "EOF" || strings.Contains(err.Error(), "connection") {
				g.Log().Println("[GTCP][ERROR]client closed", clientId)
				s.CloseSession(conn)
				break
			}
		}
	}
}

func (s *serviceServer) onConnection(conn *gtcp.Conn) error {
	if s.refuseConnect {
		return errors.New("[GTCP][ERROR]限流中，暂不接受新连接")
	}

	// 连接排队进来，每个连接中间间隔一定时间
	defer func() {
		gtimer.SetTimeout(time.Duration(3)*time.Second, func() {
			s.refuseConnect = false
		})
		if err := recover(); err != nil {
			g.Log().Println("[GTCP][onClose-panic-ERROR] = ", err)
		}
	}()

	s.refuseConnect = true

	clientId, err := s.GetClientIdByConn(conn)
	if err != nil {
		return err
	}

	// clientConnListNew := make([]*gtcp.Conn, 0)
	// clientConnListOld := clientList.Get(clientId)
	// if clientConnListOld != nil {
	// 	clientConnListNew = clientConnListOld.([]*gtcp.Conn)
	// }
	// clientConnListNew = append(clientConnListNew, conn)
	// clientList.Set(clientId, clientConnListNew)

	// // 同一个IP+端口最多只允许N个连接，超出时踢掉让客户端重新连接
	// if clientList.Size() > s.singleIpAndPortConnectLimit {
	// 	g.Log().Println("[GTCP][ERROR][onConnection]同一个IP+端口连接个数超出限制，超出时踢掉让客户端重新连接:", clientList.Size(), clientId)
	// 	s.onClose(conn)
	// 	return nil
	// }

	if s.onConnectHandler != nil {
		s.onConnectHandler(conn, clientId)
	}

	return nil
}

func (s *serviceServer) onClose(conn *gtcp.Conn) error {
	defer func() {
		gtimer.SetTimeout(time.Duration(3)*time.Second, func() {
			s.refuseConnect = false
		})
		if err := recover(); err != nil {
			g.Log().Println("[GTCP][onClose-panic-ERROR] = ", err)
		}
	}()
	s.refuseConnect = true //正在清理连接，不允许新连接接入

	clientId, err := s.GetClientIdByConn(conn)
	if err != nil {
		return err
	}
	uid := s.GetUidByClientId(clientId)

	if s.onDisConnectHandler != nil {
		s.onDisConnectHandler(conn, clientId, uid)
	}

	// if len(uid) > 0 {
	// 	uid2ClientList.Remove(uid)
	// }

	clientIds := uid2ClientList.Get(uid)
	if clientIds != nil {
		for _, cid := range clientIds.([]string) {
			conns := s.GetConnByClientId(cid)
			// if conns != nil {
			for _, conn := range conns {
				_ = conn.Close()
			}
		}
	}
	uid2ClientList.Remove(uid)

	client2UidList.Remove(clientId)
	clientList.Remove(clientId)

	return nil
}

func (s *serviceServer) CloseSession(conn *gtcp.Conn) error {
	defer func() {
		gtimer.SetTimeout(time.Duration(3)*time.Second, func() {
			s.refuseConnect = false
		})
		if err := recover(); err != nil {
			g.Log().Println("[GTCP][CloseSession-panic-ERROR] = ", err)
		}
	}()
	s.refuseConnect = true //正在清理连接，不允许新连接接入

	clientId, err := s.GetClientIdByConn(conn)
	if err != nil {
		return err
	}
	uid := s.GetUidByClientId(clientId)
	clientIds := uid2ClientList.Get(uid)
	if clientIds != nil {
		cidList := make([]string, 0)
		for _, cid := range clientIds.([]string) {
			if cid == clientId {
				client2UidList.Remove(clientId)
				conns := s.GetConnByClientId(cid)
				// if conns != nil {
				for _, conn := range conns {
					_ = conn.Close()
				}

				clientList.Remove(clientId)
				_ = conn.Close()
			} else {
				cidList = append(cidList, cid)
			}
		}
		uid2ClientList.Set(uid, cidList)
	}

	if uid2ClientList.Get(uid) == nil || len(uid2ClientList.Get(uid).([]string)) == 0 {
		uid2ClientList.Remove(uid)
		if s.onDisConnectHandler != nil {
			s.onDisConnectHandler(conn, clientId, uid)
		}
	}
	return nil
}
